
#include "http.h"
#include "tools/socket.h"
#include "tools/dump.h"
#include "tools/file_util.h"
#include "tools/string_util.h"
#include <string>
#include <fstream>
#include <sstream>
#include <map>
#include <list>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "log.h"

Http::Http(tools::CSocket* sock)
    : sock_(sock)
    , buffer(NULL)
    , buflen(0) 
{
    para_.Reset();
    codemap[200] = "OK";
    codemap[302] = "Found"; 
    codemap[404] = "Not Found";
}

Http::~Http() 
{
}

void Http::Reset(void)
{
    para_.Reset();
}

void Http::Register(std::string ext, const Http::Scripter* sc)
{
    scripter_map_[ext] = sc;
}

void Http::RegisterDefaultScripter(const Http::Scripter* sc)
{
    default_scripter_ = sc;
}

void Http::Read(const char * buf, int len)
{
    buffer = (char *)realloc(buffer, buflen + len + 1);
    if (!buffer) {
        throw ("realloc failed.");
    }
    memcpy(buffer + buflen, buf, len);
    buffer[buflen + len] = '\0';
    buflen += len;

    while (true) {
        if (para_.method == Param::METHOD_INIT) {
            char * p = strstr(buffer, "GET");
            if (!p) {
                p = strstr(buffer, "POST");
            }
            if (!p)
                break;
            char * el = strstr(buffer, "\r\n\r\n");
            if (!el)
                break;
            el+=2;
            char bak = *el;
            *el = '\0';
            int contentlen = ParseHead(p);
            *el = bak;
            len = buflen - (el - buffer);
            if (len <= 0) {
                free(buffer);
                buflen = 0;
                break;
            }
            if (contentlen > 0) {
                if (len >= contentlen) {
                    OnRequest(el, contentlen);
                    Reset();
                    el += contentlen;
                    len -= contentlen;
                }
            } else {
                OnRequest(NULL, 0);
                Reset();
            }
            p = (char *)malloc(len + 1);
            memcpy(p, el, len);
            p[len] = '\0';
            free(buffer);
            buffer = p;
            buflen = len;
        } else {
            int cl = atoi(para_.httppara["Content-Length"].c_str());
            if (cl > 0) {
                if (buflen > cl) {
                    OnRequest(buffer, cl);
                    Reset();
                    int len = buflen - cl;
                    if (len > 0) {
                        char * p = (char*)malloc(len + 1);
                        memcpy(p, buffer + cl, len);
                        free(buffer);
                        buffer = p;
                        buflen = len;
                    } else {
                        free(buffer);
                        buffer = NULL;
                        buflen = 0;
                    }
                }
            } else {
                Reset();
            }
        }
    }
}

int Http::ParseHead(char * buffer)
{
    PRINTF("parse head: \n%s\n", buffer);
    char * p = NULL;
    while ((p = strstr(buffer, "\r\n")) != NULL) {
        *p = '\0';
        char * p1 = strchr(buffer, ':');
        if (p1 != NULL) {
            *p1 = '\0';
            p1++;
            while (*p1 == ' ')
                p1++;
            PRINTF("[%s] => [%s]\n", buffer, p1);
            para_.httppara[buffer] = p1;
        } else {
            char * t = NULL;
            if (strncmp(buffer, "GET ", 4) == 0) {
                para_.method = Param::METHOD_GET;
                t = buffer + 4;
            } else if (strncmp(buffer, "POST ", 5) == 0) {
                para_.method = Param::METHOD_POST;
                t = buffer + 5;
            } else {
                PRINTF("unrecognize: [%s]\n", buffer);
            }
            if (t != NULL) {
                while (*t == ' ')
                    t++;
                char * sp = strchr(t, ' '); 
                *sp = '\0';
                para_.url = t;
                sp++;
                while (*sp == ' ')
                    sp++;
                para_.proto = sp;
            }
        }
        buffer = p + 2;
    }

    if (para_.httppara.find("Context-Length") != para_.httppara.end()) {
        return atoi(para_.httppara["Context-Length"].c_str());
    }
    return 0;
}


void Http::OnRequest(const char * content, int len)
{
    StringMap   para;
    std::string path(".");
    path += para_.url;

    size_t  pos = path.find_first_of("?");
    if (pos != std::string::npos) {
        std::string temp = path.substr(pos + 1);
        path = path.substr(0, pos);
        temp += std::string("&");
        while ((pos = temp.find_first_of("&")) != std::string::npos) {
            std::string line = temp.substr(0, pos);
            temp = temp.substr(pos + 1);
            size_t  ep = line.find_first_of("=");
            if (ep != std::string::npos) {
                para[line.substr(0, ep)] = tools::parse_url_para(line.substr(ep + 1));
            }
        }
    }
    para_.urlpara = para;

    std::string ext;
    pos = path.find_last_of(".");
    if (pos == std::string::npos) {
        ext = "NOEXT";
    } else {
        ext = path.substr(pos + 1);
    }
    
    std::map<std::string, const Scripter*>::iterator it = scripter_map_.find(ext);
    if (it != scripter_map_.end()) {
        it->second->run(this, path, para_);
    } else {
        if (default_scripter_) {
            default_scripter_->run(this, path, para_);
        }
    }

    return;
}

std::string Http::MakeHttpHead(int code, int ContextLength)
{
    char    scode[100];
    sprintf(scode, "%d", code);
    std::string head = "HTTP/1.1 ";
    head += scode;
    head += " ";
    head += codemap[code];
    head += "\r\n";
    head += "Server: kazuki's false\r\n";
    head += "Content-Type: text/html; charset=utf-8\r\n";
    if (ContextLength >= 0) {
        char temp[100];
        sprintf(temp, "Content-Length: %d\r\n", ContextLength);
        head += temp;
    }
    head += "\r\n";
    return head;
}

void Http::Reply(const char * content, int len)
{
    if (sock_) {
        sock_->Send(content, len);
        // tools::dump(content, len);
    }
}

void Http::Reply404(void)
{
    std::string tmp = "No such page: " + para_.url;
    std::string cnt = MakeHttpHead(404, tmp.length());
    cnt += tmp + "\r\n\r\n";
    Reply(cnt.c_str(), cnt.length());
}

void Http::Reply(int code, const char * content, int len)
{
    std::string cnt = MakeHttpHead(200, len);
    Reply(cnt.c_str(), cnt.length());
    Reply(content, len);
}




